package com.vito.oa.act.extend.service.impl;

import static com.vito.oa.act.constant.OaActContants.CACHE_ACT_VALUE;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import org.activiti.engine.TaskService;
import org.activiti.engine.impl.cfg.ProcessEngineConfigurationImpl;
import org.activiti.engine.impl.persistence.deploy.DeploymentManager;
import org.activiti.engine.impl.persistence.entity.TaskEntity;
import org.activiti.engine.repository.ProcessDefinition;
import org.activiti.engine.runtime.ProcessInstance;
import org.activiti.engine.task.Task;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cache.annotation.CacheEvict;
import org.springframework.cache.annotation.Cacheable;

import com.alibaba.dubbo.config.annotation.Service;
import com.vito.act.model.ActExBussModel;
import com.vito.act.model.ActExTableModel;
import com.vito.act.service.impl.ActivitiMainServiceImpl;
import com.vito.act.tool.ActivitiFormDataTool;
import com.vito.act.tool.ActivitiTool;
import com.vito.act.tool.OaActivitiFormDataTool;
import com.vito.act.tool.OaActivitiTool;
import com.vito.act.tool.ScriptHelper;
import com.vito.act.tool.ScriptToolInterface;
import com.vito.base.authoriza.model.UserModel;
import com.vito.base.model.MyHashMap;
import com.vito.base.model.Result;
import com.vito.base.util.Contants;
import com.vito.base.util.StringUtil;
import com.vito.oa.act.constant.OaActContants;
import com.vito.oa.act.constant.OaActContants.ProcessButtonEnum;
import com.vito.oa.act.dao.ActUserProcessSettingMapper;
import com.vito.oa.act.dao.OaActApproverMapper;
import com.vito.oa.act.dao.OaActCCMapper;
import com.vito.oa.act.extend.dao.OaActExBussMapper;
import com.vito.oa.act.extend.dao.OaActExDataFollowMapper;
import com.vito.oa.act.extend.dao.OaActExDataMapper;
import com.vito.oa.act.extend.dao.OaActExTableColsMapper;
import com.vito.oa.act.extend.dao.OaActExTableMapper;
import com.vito.oa.act.extend.model.OaActExTableColsModel;
import com.vito.oa.act.extend.model.OaTaskModel;
import com.vito.oa.act.extend.service.OaActExTableColsService;
import com.vito.oa.act.extend.service.OaActProgressTableService;
import com.vito.oa.act.extend.service.OaActivitiMainService;
import com.vito.oa.act.model.ActUserNodeSettingModel;
import com.vito.oa.act.model.BusinessApproverModel;

@Service(group = "OaActivitiMainServiceImpl")
public class OaActivitiMainServiceImpl extends ActivitiMainServiceImpl implements OaActivitiMainService {
	private static Logger log = LoggerFactory.getLogger(OaActivitiMainServiceImpl.class);

	@Autowired
	private OaActExTableMapper actExTableMapper;
	@Autowired
	private OaActExTableColsMapper actExTableColsMapper;
	@Autowired
	private OaActExTableColsService actExTableColsService;
	@Autowired
	private OaActProgressTableService actProgressTableService;
	@Autowired
	private OaActCCMapper oaActCCMapper;
	@Autowired
	private OaActApproverMapper oaActApproverMapper;
	@Autowired
	private OaActExDataMapper actExDataMapper;
	@Autowired
	private OaActExDataFollowMapper actExDataFollowMapper;
	@Autowired
	private OaActExBussMapper oaActExBussMapper;
	// @Autowired
	// private OaActExBussDefMapper actExBussDefMapper;
	@Autowired
	private ActUserProcessSettingMapper actUserProcessSettingMapper;

	@Override
	@Cacheable(value = CACHE_ACT_VALUE, key = "#root.method.name+'_'+#user.userId+'_'+#key+'_'+#processInstanceId")
	public Map<String, Object> getProcessPage(UserModel user, String key, String processInstanceId) throws Exception {
		String nodeId = getNodeId(user, key, processInstanceId);
		if (nodeId == null) {
			log.debug("nodeId is null,return null");
			return null;
		}
		Map<String, Object> result = getTableConfig(key, nodeId, false);// 封装表单表单
		if (processInstanceId != null && !"".equals(processInstanceId.trim())) {
			String bussId = ActivitiTool.getBusnessKey(processInstanceId);
			result.put(Contants.FORM_KEY_VIEW, actProgressTableService.findProcessView(key, bussId));
			// 封装业务id
			result.put(Contants.FORM_KEY_BUSENESSID, bussId);
		}

		ActExBussModel bussModel = new ActExBussModel();
		bussModel.setProcessKey(key);
		bussModel = oaActExBussMapper.queryActExBussByProcessKey(bussModel);
		ActUserNodeSettingModel userNode = actUserProcessSettingMapper.queryNodeSetting(key, user.getDeptParentId(),
				nodeId);
		String isSeal = userNode.getIsSeal();
		String isSign = userNode.getIsSign();
		result.put(OaActContants.NODE_IS_SEAL, isSeal);
		result.put(OaActContants.NODE_IS_SIGN, isSign);
		result.put(Contants.FORM_KEY_KEY, key);
		result.put(Contants.CURRENT_USER_ID, user.getUserId());
		result.put(Contants.FORM_KEY_NODEID, nodeId);
		result.put("businessName", bussModel.getBusinessName());
		result.put(Contants.FORM_KEY_BTNS, OaActivitiTool.getOaButtons(key, nodeId, processInstanceId));// 封装按钮
		return result;
	}

	@Override
	@Cacheable(value = CACHE_ACT_VALUE, key = "#root.method.name+'_'+#user.userId+'_'+#key+'_'+#bussId")
	public Map<String, Object> getProcessViewPage(UserModel user, String key, String bussId) {
		Map<String, Object> result = new HashMap<String, Object>();
		MyHashMap<String, String> actData = actExDataMapper.queryActExDataByBussId(bussId).get(0);
		// 封装业务id
		result.put(Contants.FORM_KEY_BUSENESSID, bussId);
		result.put(Contants.FORM_KEY_KEY, key);
		result.put("processName", actData.get("processname"));
		result.put("startUser", actData.get("userid"));
		result.put(Contants.CURRENT_USER_ID, user.getUserId());
		result.put("status", actData.get("status"));
		result.put(Contants.FORM_KEY_VIEW, actProgressTableService.findProcessView(key, bussId));
		return result;
	}

	/**
	 * @param key
	 * @param nodeId
	 * @return 根据key和nodeId 封装表的属性，以及列的属性
	 */
	@Cacheable(value = CACHE_ACT_VALUE, key = "#root.method.name+'_'+#key+'_'+#nodeId")
	public Map<String, Object> getTableConfig(String key, String nodeId) {
		Map<String, Object> map = new HashMap<String, Object>();// (Map<String,Object>)JedisTool.getMap("FIRSTVIEW:"+key+"@"+nodeId);
		if (map != null && !map.isEmpty()) {
			return map;
		}
		map.put(Contants.FORM_KEY_KEY, key);
		map.put(Contants.FORM_KEY_NODEID, nodeId);
		// 查询当前业务的当前节点下所有的表
		List<ActExTableModel> res = actExTableMapper.queryActExTableByKeyAndNode(map);
		map = new HashMap<String, Object>();
		if (res != null && res.size() > 0) {
			List<Map<String, Object>> main = new ArrayList<Map<String, Object>>();
			List<Map<String, Object>> subs = new ArrayList<Map<String, Object>>();
			for (ActExTableModel actExTableModel : res) {
				if (actExTableModel.getParentTableKey() == null || "".equals(actExTableModel.getParentTableKey())) {
					main.add(getTableMap(actExTableModel));
				} else {
					subs.add(getTableMap(actExTableModel));
				}
			}
			map.put(Contants.FORM_KEY_MAIN, main);
			map.put(Contants.FORM_KEY_SUBS, subs);
		}
		// JedisTool.setMap("FIRSTVIEW:"+key+"@"+nodeId, map);
		return map;
	}

	/**
	 * @param actExTableModel
	 * @return 封装当前变的列，及其表的属性
	 */
	public Map<String, Object> getTableMap(ActExTableModel actExTableModel) {
		Map<String, Object> tableMap = new HashMap<String, Object>();
		tableMap.put(Contants.FORM_KEY_TAB, actExTableModel);
		Map<String, Object> param = new HashMap<String, Object>();
		param.put("tableKey", actExTableModel.getTableKey());
		param.put("refKey", actExTableModel.getRefKey());
		tableMap.put(Contants.FORM_KEY_COLS, actExTableColsMapper.queryOaActExTableColsByTabKeyAndRefKey(param));
		return tableMap;
	}

	public Map<String, Object> getSomeTableMap(ActExTableModel actExTableModel) {
		Map<String, Object> tableMap = new HashMap<String, Object>();
		tableMap.put(Contants.FORM_KEY_TAB, actExTableModel);
		Map<String, Object> param = new HashMap<String, Object>();
		param.put("tableKey", actExTableModel.getTableKey());
		param.put("refKey", actExTableModel.getRefKey());
		tableMap.put(Contants.FORM_KEY_COLS, actExTableColsMapper.queryOaActExTableColsByTabKeyAndRefKey(param));
		return tableMap;
	}

	@SuppressWarnings("unchecked")
	@Override
	@CacheEvict(value = CACHE_ACT_VALUE, allEntries = true)
	public Result saveProcessData(UserModel user, Map<String, Map<String, Object>> data) {
		/**
		 * 保存流程基础数据
		 */
		Result res = new Result();
		String processInstanceId = null;
		String bussId = null;
		String key = null;
		Map<String, Object> table = actExTableColsService.getTablePkAndFks(ActivitiFormDataTool.getTableData(data));
		OaActivitiFormDataTool.generateTabkeKey(table, data, user);

		Map<String, Object> processData = ActivitiFormDataTool.generateProcessData(data);
		Map<String, Object> info = data.get(Contants.FORM_KEY_INFO);

		processInstanceId = (String) info.get(Contants.FORM_KEY_PROCESSINSTANCEID);
		key = (String) info.get(Contants.FORM_KEY_KEY);
		bussId = (String) info.get(Contants.FORM_KEY_BUSENESSID);
		// 发起申请，新建流程实例
		if (processInstanceId == null || "".equals(processInstanceId.trim())) {
			// 添加初始默认流程变量
			processData.put(OaActContants.START_USER, user.getUserId());
			processData.put(OaActContants.START_USER_DEPT, user.getDeptId());
			processData.put(OaActContants.START_USER_COMPANY, user.getDeptParentId());

			// 保存节点审批人
			Map approverMap = data.get("approverList");
			// 检查审批人
			boolean hasAssign = false;
			Object assign = info.get(OaActContants.ASSIGN);

			if (assign != null && !StringUtil.isEmpty(String.valueOf(assign))) {
				hasAssign = true;
				processData.put(OaActContants.ASSIGN, assign);
			}
			boolean hasApprover = false;
			if (approverMap.containsKey("approvers") && ((List<String>) approverMap.get("approvers")).size() > 0) {
				hasApprover = true;
				approverMap.put("businessId", bussId);
				approverMap.put("processKey", key);
				oaActApproverMapper.saveBusinessApprover(approverMap);
			}
			if (!(hasAssign | hasApprover)) {
				res.setCode(Result.CODE_PARA_ERROR);
				res.setMsg("请指定审批人");
				return res;
			}
			// 保存表单
			actProgressTableService.saveProgressTableInfo(user, data, ProcessButtonEnum.START);
			// 保存抄送数据
			if (data.get("ccUsers") != null && data.get("ccUsers").get("ccUsers") != null) {
				String ccUsers = (String) data.get("ccUsers").get("ccUsers");
				if (ccUsers != null && !"".equalsIgnoreCase(ccUsers.trim())) {
					String userIds[] = ccUsers.split(",");
					if (userIds != null && userIds.length > 0) {
						oaActCCMapper.saveCCUsers(userIds, bussId);
						processData.put(OaActContants.COPY_USER, userIds);
					}
				}
			}

			// 启动新流程
			ActivitiTool.getIdentityService().setAuthenticatedUserId(user.getUserId());
			String des = OaActivitiTool.getLastVersionProcess(key).getDescription();
			des = StringUtil.replaceLineBreak(des);
			if (des != null && !"".equals(des)) {
				ScriptToolInterface tool = (ScriptToolInterface) ScriptHelper
						.getObject("com/vito/act/tool/ScriptTool.groovy");
				if (tool != null) {
					processData.putAll(tool.runActivitiParam(des, "param", processData));
				}
			}
			log.debug("***********************************发起start***********************************");
			ProcessInstance pi = null;
			ProcessDefinition pd = OaActivitiTool.getLastVersionProcess(key);
			String lock=ActivitiTool.getProcessLock(key);
			synchronized (lock) {
				try {
					// 加锁，防止两个流程同时更改流程定义
					pi = ActivitiTool.getRuntimeService().startProcessInstanceByKey(key, bussId, processData);
					// 恢复流程定义
					OaActivitiTool.removeProcessDefinitionCache(pi.getProcessDefinitionId());

				} catch (Exception e) {
					e.printStackTrace();
				} finally {
					lock.notifyAll();
				}
			}

			// ProcessDefinition pd = ActivitiTool.getRepositoryService()
			// .getProcessDefinition(pi.getProcessDefinitionId());
			// 跟新流程实例ID
			actProgressTableService.updateInstanceByBussId(bussId, pi.getId());
			log.debug("***********************************发起end***********************************");
		} else {
			
			OaTaskModel taskModel = new OaTaskModel();
			taskModel.setBusinessId(bussId);
			taskModel.setProcessKey(key);
			taskModel.setProcessInstanceId(processInstanceId);
			taskModel.setTaskId((String) info.get(Contants.FORM_KEY_TASKID));
			taskModel.setTaskDefinitionKey((String) info.get(Contants.FORM_KEY_NODEID));
			Object assign = info.get(OaActContants.ASSIGN);
			// 防止出现同一个人同一个节点"审批"两次的问题
			this.dealWithDataNext(user, data,taskModel, assign, processData);
		}
		res.setCode(Result.CODE_OK);
		return res;
	}

	/**
	 * 保存流程基础数据
	 */
	@Override
	public Result saveProcessDataAndEndProcess(UserModel user, Map<String, Map<String, Object>> data) throws Exception {
		Result res = new Result();
		String processInstanceId = "";
		Map<String, Object> table = actExTableColsService.getTablePkAndFks(ActivitiFormDataTool.getTableData(data));
		ActivitiFormDataTool.generateTabkeKey(table, data, user);
		if (actProgressTableService.saveProgressTableInfo(user, data, null) > 0) {
			Map<String, Object> info = data.get(Contants.FORM_KEY_INFO);
			processInstanceId = (String) info.get(Contants.FORM_KEY_PROCESSINSTANCEID);

			ActivitiFormDataTool.generateProcessData(data);
			if (processInstanceId == null || "".equals(processInstanceId.trim())) {
				throw new RuntimeException();
			} else {
				String taskId = (String) info.get(Contants.FORM_KEY_TASKID);
				ActivitiTool.endProcess(taskId, null);
			}
		}
		res.setCode(Result.CODE_OK);
		return res;

	}

	@Override
	@Cacheable(value = CACHE_ACT_VALUE, key = "#root.method.name+'_'+#key+'_'+#bussId")
	public List<MyHashMap<String, String>> getProcessData(String key, String bussId) {
		return actProgressTableService.findProcessData(key, bussId);
	}

	/**
	 * 审批流转操作
	 * 
	 * @param taskModel
	 *            OaTaskModel
	 * @param assign
	 *            指派人
	 * @param processData
	 *            流程变量
	 * @throws Exception
	 */
	private boolean dealWithDataNext(UserModel user,Map<String, Map<String, Object>> data,OaTaskModel taskModel, Object assign, Map<String, Object> processData) {

		String taskId = taskModel.getTaskId();
		TaskService taskService = ActivitiTool.getTaskService();
		
		if (assign != null && !StringUtil.isEmpty(String.valueOf(assign))) {
			Object assignUser = getAssign(assign, taskId);
			if (processData == null) {
				processData = new HashMap<String, Object>();
			}
			processData.put(OaActContants.ASSIGN, assignUser);
		}

		TaskEntity task = OaActivitiTool.findTaskById(taskId);
		if (null != task) {
			ProcessDefinition pd = ActivitiTool.getRepositoryService()
					.getProcessDefinition(task.getProcessDefinitionId());
			String lock=ActivitiTool.getProcessLock(pd.getKey());
			synchronized (lock) {
				try {
					task = OaActivitiTool.findTaskById(taskId);
					if (null == task){
						return false;
					}
					actProgressTableService.saveProgressTableInfo(user, data, null);
					BusinessApproverModel upModel = oaActApproverMapper.queryaCurrentApprover(taskModel.getBusinessId(),
							taskModel.getTaskDefinitionKey());
					if (upModel != null) {
						if (String.valueOf(OaActContants.BASE_STATE)
								.equals(String.valueOf(processData.get(OaActContants.ROLL_BACK)))) {
							// 若为驳回操作，将本节点审批状态置：待审批
							upModel.setApproveStatus(OaActContants.APPROVE_STATUS_WAIT);
						} else {
							upModel.setApproveStatus(OaActContants.APPROVE_STATUS_APPROVED);
						}
						oaActApproverMapper.updateApproveStatus(upModel);
					}
					
					taskService.complete(taskId, processData);
					OaActivitiTool.removeProcessDefinitionCache(task.getProcessDefinitionId());

				} catch (Exception e) {
					e.printStackTrace();
					return false;
				} finally {
					lock.notifyAll();
				}
			}
			
			
			return true;

		}
		return false;
	}

	@SuppressWarnings("unchecked")
	private Object getAssign(Object assign, String taskId) {
		TaskService taskService = ActivitiTool.getTaskService();
		Object var = taskService.getVariable(taskId, OaActContants.ASSIGN);
		do {
			if (null == assign || StringUtil.isEmpty(String.valueOf(assign))) {
				break;
			}

			String[] subStr = ((String) assign).split(OaActContants.SEPARATOR);
			if (subStr.length > 1) { // 有多个审批人
				assign = Arrays.asList(subStr);
			}

			if (null == var && null != assign) {
				var = assign;
				break;
			}

			if (var instanceof List) {
				List<String> varList = new ArrayList((List<String>) var);
				if (assign instanceof List) {
					for (String userId : ((List<String>) assign)) {
						if (!(varList).contains(userId)) { // 去重
							(varList).add(userId);
						}
					}
				}
				if (assign instanceof String) {
					if (!(varList).contains(assign)) { // 去重
						(varList).add((String) assign);
					}
				}
				var = varList;
			}

			if (var instanceof String && assign instanceof List) {
				if (!((List<String>) assign).contains(var)) { // 去重
					((List<String>) assign).add((String) var);
				}
				var = assign;
			}

			if (var instanceof String && assign instanceof String) {
				if (!var.equals(assign)) { // 去重
					List<String> temp = new ArrayList<String>();
					temp.add((String) var);
					temp.add((String) assign);
					var = temp;
				} else {
					List<String> temp = new ArrayList<String>();
					temp.add((String) assign);
					var = temp;
				}
			}

		} while (false);
		return var;
	}

	@Override
	@CacheEvict(value = CACHE_ACT_VALUE, allEntries = true)
	public void transfer(String taskId, String userId) {
		TaskService taskService = ActivitiTool.getTaskService();
		Task t = taskService.createTaskQuery().taskId(taskId).singleResult();
		String nodeId = t.getTaskDefinitionKey();
		String instId = t.getProcessInstanceId();
		String businessId = ActivitiTool.getBusnessKey(instId);
		// 修改本节点审批状态：转交
		BusinessApproverModel currentAssignee = oaActApproverMapper.queryaCurrentApprover(businessId, nodeId);
		currentAssignee.setApproveStatus(OaActContants.APPROVE_STATUS_TRANSFER);
		oaActApproverMapper.updateApproveStatus(currentAssignee);

		Map<String, Object> follow = new HashMap<String, Object>();
		follow.put("busenessId", businessId);
		follow.put("processKey", t.getTaskDefinitionKey());
		follow.put("processNodeId", nodeId);
		follow.put("taskId", taskId);
		follow.put("action", ProcessButtonEnum.TRANSFER.nameCh);
		follow.put("userId", t.getAssignee());
		actExDataFollowMapper.saveActExDataFollow(follow);

		// 改变当前任务受理人
		taskService.setAssignee(taskId, userId);
	}

	@Override
	@CacheEvict(value = CACHE_ACT_VALUE, allEntries = true)
	public void fasterCommit(UserModel user, String businessId, String taskId) throws Exception {
		Map<String, Map<String, Object>> data = packSimpleCommitForm(businessId, taskId);
		this.saveProcessData(user, data);
	}

	@Override
	@CacheEvict(value = CACHE_ACT_VALUE, allEntries = true)
	public void fasterEnd(UserModel user, String businessId, String taskId) {
		Map<String, Map<String, Object>> data = packSimpleCommitForm(businessId, taskId);
		actProgressTableService.endProcess(user, data);

	}

	@SuppressWarnings("unchecked")
	@Override
	public Map<String, Map<String, Object>> packSimpleCommitForm(String businessId, String taskId) {
		Map<String, Map<String, Object>> result = new HashMap<>();

		TaskService taskService = ActivitiTool.getTaskService();
		Task task = taskService.createTaskQuery().taskId(taskId).singleResult();
		ProcessDefinition process = ActivitiTool.getRepositoryService()
				.getProcessDefinition(task.getProcessDefinitionId());
		String nodeId = task.getTaskDefinitionKey();
		String key = process.getKey();

		Map<String, Object> info = new HashMap<>();
		info.put(Contants.FORM_KEY_KEY, key);
		info.put(Contants.FORM_KEY_BUSENESSID, businessId);
		info.put(Contants.FORM_KEY_NODEID, nodeId);
		info.put(Contants.FORM_KEY_TASKID, taskId);
		info.put(Contants.FORM_KEY_PROCESSINSTANCEID, task.getProcessInstanceId());
		result.put(Contants.FORM_KEY_INFO, info);

		Map<String, Object> form = this.getTableConfig(key, nodeId);
		List<Map<String, Object>> subsForm = (List<Map<String, Object>>) form.get(Contants.FORM_KEY_SUBS);

		Map<String, Object> subsMap = new HashMap<>();
		for (Map<String, Object> sub : subsForm) {
			ActExTableModel table = (ActExTableModel) sub.get(Contants.FORM_KEY_TAB);
			List<OaActExTableColsModel> cols = (List<OaActExTableColsModel>) sub.get(Contants.FORM_KEY_COLS);
			List<Map<String, String>> colDatas = new ArrayList<>();
			for (OaActExTableColsModel col : cols) {
				Map<String, String> data = new HashMap<>();
				if (Contants.BASE_STATE.equals(col.getIsPk()) || Contants.BASE_STATE.equals(col.getIsFk())) {
					continue;
				}
				data.put(col.getColName(), null);
				colDatas.add(data);
			}
			Map<String, Object> subData = new HashMap<>();
			subData.put(Contants.FORM_KEY_TAB, table.getTableKey());
			subData.put(OaActContants.FORM_OPER_TYPE, table.getOperType());
			subData.put(Contants.FORM_KEY_DATA, colDatas);
			subsMap.put(table.getTableKey(), subData);
		}
		result.put(Contants.FORM_KEY_SUBS, subsMap);
		return result;
	}
}
